package com.ruoyi.pcj.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mashuai.utils.base.MsAssertUtils;
import com.mashuai.utils.toolkit.core.msCollection.MsMap;
import com.mashuai.utils.toolkit.core.msConverts.MsStreamUtils;
import com.mashuai.utils.toolkit.core.msConverts.MsTimeUtils;
import com.mashuai.utils.toolkit.core.msHandlebean.MsHandleDataUtils;
import com.mashuai.utils.toolkit.core.msMp.MsDb;
import com.ruoyi.basis.service.MsBaseServiceImpl;
import com.ruoyi.basis.utils.mp.MpConditionUtils;
import com.ruoyi.basis.utils.mp.MpPageUtils;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.pcj.constant.TipConstant;
import com.ruoyi.pcj.constant.YmlConstant;
import com.ruoyi.pcj.domain.*;
import com.ruoyi.pcj.dto.match.MatchUserAttrDto;
import com.ruoyi.pcj.mapper.PcjPpRecordsMapper;
import com.ruoyi.pcj.service.*;
import com.ruoyi.pcj.utils.pcj.PcjUtils;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 匹配-记录Service业务层处理
 *
 * @author ms
 * @date 2024-03-14
 */
@AllArgsConstructor
@Service
public class PcjPpRecordsServiceImpl extends MsBaseServiceImpl<PcjPpRecordsMapper, PcjPpRecords> implements IPcjPpRecordsService {

    private IPcjUserAttrService userAttrService;

    private IPcjLtFriendListService ltFriendListService;

    private IPcjUserTargetService userTargetService;

    private IPcjUserHobbyService userHobbyService;

    private IPcjUserAbleService userAbleService;

    // private IPcjPpRecordsService ppRecordsService;

    @Override
    public <E extends IPage<PcjPpRecords>> E page(E page, Wrapper<PcjPpRecords> qw) {
        return baseMapper.selectPage(page, qw);
    }

    /**
     * 查询匹配-记录列表
     *
     * @param params 聊天室-查询参数 {"pageNum":1,"pageSize":10}
     * @return 匹配-记录
     */
    @Override
    public IPage<PcjPpRecords> selectPcjPpRecordsList(Map<String, Object> params) {
        //获取条件构造器
        QueryWrapper<PcjPpRecords> qw = MpConditionUtils.getQueryWrapper(params, PcjPpRecords.class);
        //分页查询
        Page<PcjPpRecords> page = this.page(MpPageUtils.getPage(params), qw);
        return page;
    }


    /**
     * 根据id查询匹配-记录
     *
     * @param id 匹配-记录主键
     * @return 匹配-记录
     */
    @Override
    public PcjPpRecords selectPcjPpRecordsById(String id) {
        //首先获取主表信息
        PcjPpRecords pcjPpRecords = MsDb.getById(id, PcjPpRecords.class);
        return pcjPpRecords;
    }

    /**
     * 回填子表信息
     **/

    /**
     * 新增匹配-记录
     *
     * @param pcjPpRecords 匹配-记录
     * @return 结果
     */
    @Override
    public Boolean insertPcjPpRecords(PcjPpRecords pcjPpRecords) {
        //插入主表
        Boolean success = this.insertData(pcjPpRecords);
        return success;
    }

    /**
     * 修改匹配-记录
     *
     * @param pcjPpRecords 匹配-记录
     * @return 结果
     */
    @Override
    public Boolean updatePcjPpRecords(PcjPpRecords pcjPpRecords) {
        //修改主表
        Boolean success = this.updateData(pcjPpRecords);
        return success;
    }

    /**
     * 批量删除匹配-记录
     *
     * @param ids 需要删除的匹配-记录主键
     * @return 结果
     */
    @Override
    public Boolean deletePcjPpRecordsByIds(String[] ids) {

        //删除主表
        Boolean success = this.removeBatchByIds(Arrays.asList(ids));
        return success;
    }
    /**
     * 根据主表id，删除子表所有数据
     **/
    /**
     * 对外提供的设置默认字段值，id为空新增，不为空更新
     **/
    @Override
    public void setEntityDefValAuto(PcjPpRecords pcjPpRecords) {
        this.setDefFieldVal(pcjPpRecords);
    }


    /**
     * @param list: 与匹配用户兴趣、特长相匹配的用户信息
     * @param ls:   匹配用户兴趣、特长集合
     * @return key=用户id，value=兴趣、特长id
     * @author mashuai
     */
    public static List<Long> getMatchUserResultData(List<MatchUserAttrDto> list, List<String> ls) {
        //下面语法意思如下
        //list = [{"userId":1L,"bizId":"a1"},{"userId":1L,"bizId":"a2"},{"userId":2L,"bizId":"a1"},{"userId":3L,"bizId":"a1"},,{"userId":3L,"bizId":"a2"}}
        //ls = ["a1","a2"]
        //基于ls从list中找出出现次数最多的userId，比如userId = 1L 和3L 都出现先过两次a1,a2值，所以最终返回是 [1L,3L]
        Map<Long, Long> countMap = list.stream()
                .filter(user -> ls.contains(user.getBizId()))
                .collect(Collectors.groupingBy(MatchUserAttrDto::getUserId, Collectors.counting()));

        long maxCount = countMap.values().stream()
                .max(Long::compare)
                .orElse(0L);

        return countMap.entrySet().stream()
                .filter(entry -> entry.getValue() == maxCount)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }


    /**
     * 获取所有目标信息，key=目标id，value=目标对象
     **/
    private Map<String, PcjTarget> getTargetData() {
        List<PcjTarget> list = MsDb.list(PcjTarget.class);
        Map<String, PcjTarget> targetMap = MsStreamUtils.groupByT(list, PcjTarget::getId);
        return targetMap;
    }

    /**
     * 获取所有兴趣信息，key=兴趣id，value=兴趣对象
     **/
    private Map<String, PcjHobby> getHobbyData() {
        List<PcjHobby> list = MsDb.list(PcjHobby.class);
        Map<String, PcjHobby> hobbytMap = MsStreamUtils.groupByT(list, PcjHobby::getId);
        return hobbytMap;
    }

    /**
     * 获取所有兴趣信息，key=擅长id，value=擅长对象
     **/
    private Map<String, PcjAble> getAbleData() {
        List<PcjAble> list = MsDb.list(PcjAble.class);
        Map<String, PcjAble> ableMap = MsStreamUtils.groupByT(list, PcjAble::getId);
        return ableMap;
    }

    /**
     * 获取当前用户属性，判断匹配次数是否超过限制
     **/
    private void checkMatchCount(Long userId) {
        PcjUserAttr userAttr = userAttrService.getUserAttrByUserId(userId);
        Integer mateCount = userAttr.getMateCount();
        if (mateCount <= 0) MsAssertUtils.throwException("匹配次数不足，无法匹配");
    }


    /**
     * 匹配逻辑重构。
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object getMatchResult2() {
        //当前用户id
        Long curUserId = SecurityUtils.getUserId();
        checkMatchCount(curUserId);
        PcjSysUser sysUser = MsDb.getById(curUserId, PcjSysUser.class);
        //优先筛选出已经存在的好友、已经匹配过的好友id
        List<Long> excludeFriendIds = getExcludeFriendIds(curUserId);
        //----1.处理目标一致的用户
        Map<String, PcjTarget> allTargetData = getTargetData();
        //找出当前匹配的所有目标信息-包含特殊的目标id
        List<String> curUserTargetIds = userTargetService.getCurUserTargetIds(curUserId, allTargetData);
        //找出与当前匹配用户目标一致的用户信息
        List<MatchUserAttrDto> accordTargetUserMsg = userTargetService.getAccordTargetUserIds(excludeFriendIds, curUserTargetIds);
        //------2.进一步筛选出符合兴趣的用户
        Map<String, PcjHobby> allHobbyData = getHobbyData();
        //获取当前匹配用户的所有兴趣
        List<String> curUserHobbyIds = userHobbyService.getCurUserHobbyIds(curUserId);
        //找出与当前匹配用兴趣一致的用户
        List<MatchUserAttrDto> accordHobbyUserMsg = userHobbyService.getAccordHobbyUserIds(accordTargetUserMsg, curUserHobbyIds);
        //------3.需要对僵尸用户进行过滤
        List<MatchUserAttrDto> filterUserHobbyMsg = filterZombieUser(accordHobbyUserMsg);
        Map<Long, List<MatchUserAttrDto>> filterHobbyUserMap = MsStreamUtils.groupBy(filterUserHobbyMsg, MatchUserAttrDto::getUserId);
        //返回结果
        PcjPpRecords pcjPpRecords;
        //如果此次只有一个用户符合条件，即直接返回匹配结果，
        if (filterHobbyUserMap.size() == 1) {
            pcjPpRecords = ppHobbyResult(sysUser, filterHobbyUserMap, allHobbyData, accordTargetUserMsg, allTargetData);
        }
        //依然存在多个用户符合目标，此次进入下一个特长匹配
        else {
            //------4.进一步筛选用户所存在的特长
            Map<String, PcjAble> allAbleData = getAbleData();
            //获取当前用户的特长
//            List<String> curUserAbleIds = userAbleService.getUserAbleIds(curUserId);
            //获取与匹配用户特长一致的用户信息
            List<MatchUserAttrDto> accordAbleUserMsg = userAbleService.getMatchUserAbleMsg(curUserId, allAbleData, filterUserHobbyMsg);
            //---如果特长没有符合的，则直接返回兴趣一层
            if (accordAbleUserMsg.size() == 0) {
                //找出兴趣与匹配用户的兴趣数量最多那个人
                List<Long> maxHobbyNumUserIds = getMatchUserResultData(accordHobbyUserMsg, curUserHobbyIds);
                //如果只有一个兴趣数量最大值，达成匹配
                if (maxHobbyNumUserIds.size() == 1) {
                    pcjPpRecords = ppHobbyResult(sysUser, getHobbyResultZjData(maxHobbyNumUserIds.get(0), filterHobbyUserMap), allHobbyData, accordTargetUserMsg, allTargetData);
                }
                //如果此次存在多个用户兴趣数量任然一样，直接取登录时间最近的那个用户即可
                else {
                    Comparator<MatchUserAttrDto> comparator = Comparator.comparing(MatchUserAttrDto::getLoginTime).reversed();
                    Collections.sort(accordHobbyUserMsg, comparator);
                    pcjPpRecords = ppHobbyResult(sysUser, getHobbyResultZjData(accordHobbyUserMsg.get(0).getUserId(), filterHobbyUserMap), allHobbyData, accordTargetUserMsg, allTargetData);
                }
            }
            //---如果用户特长与当前匹配用户一致.
            else {
                //判断当前特长一致的用户存在几个
                List<Long> ableUserIds = accordAbleUserMsg.stream().map(t -> t.getUserId()).distinct().collect(Collectors.toList());
                //如果此时仅仅存在一个用户，那么为本次匹配结果。
                if (ableUserIds.size() == 1) {
                    Long mtId = ableUserIds.get(0);
                    pcjPpRecords = handleUserResult(sysUser, accordAbleUserMsg, accordHobbyUserMsg, accordTargetUserMsg, mtId, allAbleData, allHobbyData, allTargetData);
                }
                //如果存在多个用户特长都一样，反查兴趣数量，如果数量一致，优先返回最新的在线时间
                else {
                    //以特长为基础从兴趣集合过滤
                    List<MatchUserAttrDto> hobbyUserMsgList = accordHobbyUserMsg.stream().filter(t -> ableUserIds.contains(t.getUserId())).collect(Collectors.toList());
                    List<Long> maxHobbyNumUserIds = getMatchUserResultData(hobbyUserMsgList, curUserHobbyIds);
                    if (maxHobbyNumUserIds.size() == 1) {
                        pcjPpRecords = handleUserResult(sysUser, accordAbleUserMsg, accordHobbyUserMsg, accordTargetUserMsg, maxHobbyNumUserIds.get(0), allAbleData, allHobbyData, allTargetData);
                    } else {
                        Comparator<MatchUserAttrDto> comparator = Comparator.comparing(MatchUserAttrDto::getLoginTime).reversed();
                        Collections.sort(accordHobbyUserMsg, comparator);
                        Long mtId = accordHobbyUserMsg.get(0).getUserId();
                        pcjPpRecords = handleUserResult(sysUser, accordAbleUserMsg, accordHobbyUserMsg, accordTargetUserMsg, mtId, allAbleData, allHobbyData, allTargetData);
                    }
                }

            }
        }
        //匹配字数-1
        //userAttrService.updateMatchCount(curUserId, -1);
        //插入匹配记录，
        // this.insertData(pcjPpRecords);
        return pcjPpRecords;
    }


    /**
     * 处理数据
     **/
    private PcjPpRecords handleUserResult(PcjSysUser sysUser,
                                          List<MatchUserAttrDto> accordAbleUserMsg,
                                          List<MatchUserAttrDto> accordHobbyUserMsg,
                                          List<MatchUserAttrDto> accordTargetUserMsg,
                                          Long mtId, Map<String, PcjAble> allAbleData, Map<String, PcjHobby> allHobbyData, Map<String, PcjTarget> allTargetData) {
        //特长名称
        List<String> ableNameList = new ArrayList<>();
        for (MatchUserAttrDto able : accordAbleUserMsg) {
            ableNameList.add(allAbleData.get(able.getBizId()).getName());
        }
        String ableNames = ableNameList.stream().collect(Collectors.joining("、"));//getMatchResultName(accordAbleUserMsg, mtId, allAbleData, PcjAble::getName);
        //对兴趣爱好进行过滤
        String hobbyNames = getMatchResultName(accordHobbyUserMsg, mtId, allHobbyData, PcjHobby::getName);
        //在过滤出目标名称
        String targetNames = getMatchResultName(accordTargetUserMsg, mtId, allTargetData, PcjTarget::getName);
        //消息
        //构建提示语
        String msg = "您好！我是" + sysUser.getNickName() + "。我的目标是：" + targetNames
                + "。我们在" + hobbyNames + "方面兴趣相同。我擅长：" + ableNames + "。我们不妨先从我们的共同兴趣开始聊一聊吧！";
        //构建匹配记录
        PcjPpRecords ppRecords = new PcjPpRecords();
        this.setEntityDefValAuto(ppRecords);
        ppRecords.setType("0");
        ppRecords.setUserId(sysUser.getUserId());
        ppRecords.setPpUserId(mtId);
        ppRecords.setPpChatMsg(msg);
        return ppRecords;


    }

    /**
     * 获取集合中的名字
     */
    private <T> String getMatchResultName(List<MatchUserAttrDto> matchMsg, Long mtId, Map<String, T> allHobbyData, Function<T, String> fuc) {
        String name = matchMsg.stream().filter(t -> t.getUserId() == mtId).map(t -> {
            String bizId = t.getBizId();
            return fuc.apply(allHobbyData.get(bizId));
        }).collect(Collectors.joining("、"));
        return name;
    }

    /**
     * 爱好-->目标，确认唯一用户时候
     *
     * @param filterHobbyUserMap 只存在一个用户。
     **/
    private PcjPpRecords ppHobbyResult(PcjSysUser sysUser,
                                       Map<Long, List<MatchUserAttrDto>> filterHobbyUserMap, Map<String, PcjHobby> allHobbyData,
                                       List<MatchUserAttrDto> accordTargetUserMsg, Map<String, PcjTarget> allTargetData) {
        //兴趣名称
        List<String> hobbyNameList = new ArrayList<>();
        //目标名称
        List<String> targetNameList = new ArrayList<>();
        Long matchUserId = null;
        //优先筛选出兴趣
        for (Long userId : filterHobbyUserMap.keySet()) {
            matchUserId = userId;
            List<MatchUserAttrDto> dto = filterHobbyUserMap.get(userId);
            for (MatchUserAttrDto hobby : dto) {
                hobbyNameList.add(allHobbyData.get(hobby.getBizId()).getName());
            }
            for (MatchUserAttrDto target : accordTargetUserMsg.stream().filter(t -> t.getUserId() == userId).collect(Collectors.toList())) {
                targetNameList.add(allTargetData.get(target.getBizId()).getName());
            }
            break;
        }
        //构建提示语
        String msg = "您好！我是" + sysUser.getNickName() + "。我的目标是：" + targetNameList.stream().collect(Collectors.joining("、"))
                + "。我们在" + hobbyNameList.stream().collect(Collectors.joining("、")) + "方面兴趣相同。我们不妨先从我们的共同兴趣开始聊一聊吧！";

        //构建匹配记录
        PcjPpRecords ppRecords = new PcjPpRecords();
        this.setEntityDefValAuto(ppRecords);
        ppRecords.setType("0");
        ppRecords.setUserId(sysUser.getUserId());
        ppRecords.setPpUserId(matchUserId);
        ppRecords.setPpChatMsg(msg);
        return ppRecords;

    }

    /**
     * 方法处理，从兴趣匹配结果中取出最符合条件的用户
     **/
    private Map<Long, List<MatchUserAttrDto>> getHobbyResultZjData(Long mtUserId, Map<Long, List<MatchUserAttrDto>> filterHobbyUserMap) {
        Map<Long, List<MatchUserAttrDto>> zjHobbyPpMap = new HashMap<>();
        zjHobbyPpMap.put(mtUserId, filterHobbyUserMap.get(mtUserId));
        return zjHobbyPpMap;
    }

    /**
     * 过滤出僵尸用户
     **/
    private List<MatchUserAttrDto> filterZombieUser(List<MatchUserAttrDto> accordHobbyUserMsg) {
        Date nowTime = new Date();
        MsHandleDataUtils.handleTbOrDictValBeans(accordHobbyUserMsg, MatchUserAttrDto.class);
        //第一重过滤，取最近2h在线的
        List<MatchUserAttrDto> firstList = new ArrayList<>();
        //第二重过滤，取最近24小时在线
        List<MatchUserAttrDto> secondList = new ArrayList<>();
        //第三重过滤，取最近24小时在线
        List<MatchUserAttrDto> thirdList = new ArrayList<>();
        //优先筛选出符合2h内的用户
        for (MatchUserAttrDto dto : accordHobbyUserMsg) {
            LocalDateTime loginTime = dto.getLoginTime();
            if (loginTime != null) {
                Date loginDate = Date.from(loginTime.atZone(ZoneId.systemDefault()).toInstant());
                //判断与当前匹配时间相差是否在2h内
                Long hour = MsTimeUtils.calculateDateBetween(loginDate, nowTime, "hour");
                if (hour <= YmlConstant.ppFirstLimit) {
                    firstList.add(dto);
                }
                //2<当前时间<=24小时
                if (hour > YmlConstant.ppFirstLimit && hour <= YmlConstant.ppSecondLimit) {
                    secondList.add(dto);
                }
                //如果>24h
                if (hour > YmlConstant.ppSecondLimit && hour <= YmlConstant.ppThirdLimit) {
                    thirdList.add(dto);
                }
            }
        }
        //如果未匹配到三天内的直接，匹配失败
        if (firstList.size() == 0 && secondList.size() == 0 && thirdList.size() == 0) {
            MsAssertUtils.throwException(TipConstant.MATCH_NO_72_HOUR);
        }
        //如果2h内存在符合的用户，优先选择
        if (firstList.size() != 0) {
            return firstList;
        }
        //如果2h不存在，优先返回24h内的
        if (secondList.size() != 0) {
            return secondList;
        }
        return thirdList;
    }


    /**
     * 获取需要排除的好友id，已经匹配过，并且不在好友列表的被排除
     */
    public List<Long> getExcludeFriendIds(Long userId) {
        //好友列表的id
        List<PcjLtFriendList> firendList = ltFriendListService.getFirendList(userId);
        List<Long> friendIds = MsStreamUtils.listStreamMap(firendList, PcjLtFriendList::getFriendId);
        //已经被匹配过的好友
        List<PcjPpRecords> ppRecordList = PcjUtils.getDataByUserId(userId, PcjPpRecords.class);
        List<Long> ppUserIds = MsStreamUtils.listStreamMap(ppRecordList, PcjPpRecords::getPpUserId);
        //获取已经关闭匹配的用户
        LambdaQueryWrapper<PcjUserAttr> qw = Wrappers.lambdaQuery();
        qw.eq(PcjUserAttr::getClosePp, "1");
        List<PcjUserAttr> userAttrList = MsDb.list(qw, PcjUserAttr.class);
        if (userAttrList.size() != 0) {
            friendIds.addAll(MsStreamUtils.listStreamMap(userAttrList, PcjUserAttr::getSysUserId));
        }
        //两个集合汇总
        friendIds.addAll(ppUserIds);
        //加上自己
        friendIds.add(userId);
        if (friendIds.size() == 0) return friendIds;
        //去重
        return friendIds.stream().distinct().collect(Collectors.toList());
    }

}
